package com.atguigu.gmall.cart.service;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.feign.GmallPmsClient;
import com.atguigu.gmall.cart.feign.GmallSmsClient;
import com.atguigu.gmall.cart.feign.GmallWmsClient;
import com.atguigu.gmall.cart.interceptor.LoginInterceptor;
import com.atguigu.gmall.cart.mapper.CartMapper;
import com.atguigu.gmall.cart.pojo.Cart;
import com.atguigu.gmall.cart.pojo.UserInfo;
import com.atguigu.gmall.common.bean.ResponseVo;
import com.atguigu.gmall.common.exception.CartException;
import com.atguigu.gmall.common.exception.OrderException;
import com.atguigu.gmall.pms.entity.SkuAttrValueEntity;
import com.atguigu.gmall.pms.entity.SkuEntity;
import com.atguigu.gmall.sms.vo.ItemSaleVo;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.concurrent.ListenableFuture;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CartService {

    @Autowired
    private CartAsyncService cartAsyncService;

    @Autowired
    private GmallPmsClient pmsClient;

    @Autowired
    private GmallSmsClient smsClient;

    @Autowired
    private GmallWmsClient wmsClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String KEY_PREFIX = "cart:info:";

    private static final String PRICE_PREFIX = "cart:price:";
    public void saveCart(Cart cart) {
        String userId = this.getUserId();

        // 2 判断当前用户是否有该商品的购物车 没有则新增 有则更新数量 count  opsForHash() Map<skuId,cartJson>
        //根据userId 获取内层Map
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(KEY_PREFIX + userId);

        //获取skuId
        String skuId = cart.getSkuId().toString();
        BigDecimal count = cart.getCount(); //本次新增的数量
        //购物车以存在该商品 更新数量 redis 和 mysql
        if (hashOps.hasKey(skuId)) {
            //存在 更新数量 通过内层的key skuId 获取购物车字符串
            String cartJson = hashOps.get(skuId).toString();
            cart = JSON.parseObject(cartJson, Cart.class);
            //增加数量
            cart.setCount(cart.getCount().add(count));
            //更新mysql数据库
            cartAsyncService.updateMysql(userId,cart,  skuId);
        }else {
            //不存在 新增购物车
            cart.setUserId(userId);
            cart.setCheck(true);

            //根据skuId查询sku
            ResponseVo<SkuEntity> skuEntityResponseVo = pmsClient.querySkuById(cart.getSkuId());
            SkuEntity skuEntity = skuEntityResponseVo.getData();
            if (skuEntity == null){
                //商品不存在 无法加入购物车 抛出异常
                throw new CartException("您要加入购物车的商品不存在!");
            }
            //设置基本属性
            cart.setTitle(skuEntity.getTitle());
            cart.setDefaultImage(skuEntity.getDefaultImage());
            cart.setPrice(skuEntity.getPrice());

            //是否有货 库存
            ResponseVo<List<WareSkuEntity>> wareResponseVo = wmsClient.queryWareSkusBySkuId(cart.getSkuId());
            List<WareSkuEntity> wareSkuEntities = wareResponseVo.getData();
            if (!CollectionUtils.isEmpty(wareSkuEntities)){
                cart.setStore(wareSkuEntities.stream().anyMatch(wareSkuEntity -> wareSkuEntity.getStock() - wareSkuEntity.getStockLocked()>0));
            }

            //设置销售属性
            ResponseVo<List<SkuAttrValueEntity>> skuAttrResponseVo = pmsClient.querySkuAttrsBySkuId(cart.getSkuId());
            List<SkuAttrValueEntity> skuAttrValueEntities = skuAttrResponseVo.getData();
            cart.setSaleAttrs(JSON.toJSONString(skuAttrValueEntities));

            //设置营销信息
            ResponseVo<List<ItemSaleVo>> itemSaleResponseVo = smsClient.queryItemSalesBySkuId(cart.getSkuId());
            List<ItemSaleVo> itemSaleVos = itemSaleResponseVo.getData();
            cart.setSales(JSON.toJSONString(itemSaleVos));

            //保存到数据库
            cartAsyncService.insertMysql(cart.getSkuId().toString(),cart);

            // 新增时 缓存最新价格
            redisTemplate.opsForValue().set(PRICE_PREFIX+skuId,skuEntity.getPrice().toString());
        }

        //保存到redis
        hashOps.put(skuId,JSON.toJSONString(cart));
    }


    public Cart queryCart(Long skuId) {
        //获取登录状态
        String userId = this.getUserId();
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(KEY_PREFIX+userId);
        //判断是否存在该记录
        if (hashOps.hasKey(skuId.toString())){
            String cartJson = hashOps.get(skuId.toString()).toString();
            return JSON.parseObject(cartJson,Cart.class);
        }
        throw new CartException("您的购物车没有该商品!");
    }

    private String getUserId() {
        // 1 获取登录状态 登录-userId 未登录userKey
        UserInfo userInfo = LoginInterceptor.getUserInfo();

        // 定义一个userId 默认取userKey
        String userId = userInfo.getUserKey();
        //如果userId不为空 则用userId替代userKey
        if (userInfo.getUserId()!= null){
            userId = userInfo.getUserId().toString();
        }
        return userId;
    }

    /**
     * 查询购物车
     * @return
     */
    public List<Cart> queryCarts() {

        // 获取 登录状态中的userKey
        UserInfo userInfo = LoginInterceptor.getUserInfo();
        String userKey = userInfo.getUserKey();

        // 1 根据userKey 查询未登录购物车 map<skuId,cartJson>
        BoundHashOperations<String, Object, Object> unLoginHashOps = redisTemplate.boundHashOps(KEY_PREFIX + userKey);

        //获取未登录购物车列表
        List<Object> cartJsons = unLoginHashOps.values();

        List<Cart> unLoginCarts = null;
        //将json字符串反序列化为购物车集合
        if (!CollectionUtils.isEmpty(cartJsons)){
                unLoginCarts = cartJsons.stream().map(cartJson -> {
                Cart cart = JSON.parseObject(cartJson.toString(), Cart.class);
                    //设置实时价格
                    BigDecimal currentPrice = new BigDecimal(redisTemplate.opsForValue().get(PRICE_PREFIX + cart.getSkuId()));
                    cart.setCurrentPrice(currentPrice);
                    return cart;
            }).collect(Collectors.toList());
        }
        //获取userId
        Long userId = userInfo.getUserId();
        if (userId == null){
            // 2 如果userId 为空直接返未登录购物车
            return unLoginCarts;
        }

        // 3 如果userId 不为空 查询已登录购物车
        //查询已登录购物车
        BoundHashOperations<String, Object, Object> loginHashOps = redisTemplate.boundHashOps(KEY_PREFIX + userId);
        // 4 并合并未登录购物车 如果未登录购物车不为空 则合并
        if (!CollectionUtils.isEmpty(unLoginCarts)){
            //遍历每一条未登录购物车 判断是否包含
            unLoginCarts.forEach(cart -> {
                String skuId = cart.getSkuId().toString();
                //获取新增数量 未登录购物车数量
                BigDecimal count = cart.getCount();
                if(loginHashOps.hasKey(skuId)){
                    //包含 则新增数量
                    String cartJson = loginHashOps.get(skuId).toString();
                     cart = JSON.parseObject(cartJson, Cart.class);
                     //新增数量
                     cart.setCount(cart.getCount().add(count));
                    //保存到mysql
                    cartAsyncService.updateMysql(userId.toString(),cart,skuId);
                }else {
                    //不包含 新增记录
                    //修改userKey为userId
                    cart.setUserId(userId.toString());
                    //设置主键为空 避免冲突
                    cart.setId(null);
                    //保存到mysql
                    cartAsyncService.insertMysql(cart.getSkuId().toString(),cart);
                }

                //保存到redis
                loginHashOps.put(skuId,JSON.toJSONString(cart));
            });
        }

        // 5 删除未登录购物车
        // 删除redis中的记录
        redisTemplate.delete(KEY_PREFIX+userKey);
        // 删除mysql中的记录
        cartAsyncService.deleteMysql(userKey);
        // 6 返回合并后的购物车
        List<Object> loginCartJsons = loginHashOps.values();
        if (!CollectionUtils.isEmpty(loginCartJsons)){
            return loginCartJsons.stream().map(loginCartJson->{
                Cart cart = JSON.parseObject(loginCartJson.toString(),Cart.class);
                //设置实时价格
                BigDecimal currentPrice = new BigDecimal(redisTemplate.opsForValue().get(PRICE_PREFIX + cart.getSkuId()));
                cart.setCurrentPrice(currentPrice);
                return cart;
            }).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 更新购物车数量
     * @param cart
     */
    public void updateNum(Cart cart) {
        //获取登录状态 未登录 userKsy 已登录 userId
        String userId = this.getUserId();
        //根据userId查询修改记录
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(KEY_PREFIX + userId);
        Long skuId = cart.getSkuId();
        //获取增加的数量
        BigDecimal count = cart.getCount();
        //判断是否存在该 skuId的商品
        if (hashOps.hasKey(skuId.toString())){
            String cartJson = hashOps.get(skuId.toString()).toString();
            //覆盖页面的cart
            cart = JSON.parseObject(cartJson,Cart.class);
            //设置最新的数量
            cart.setCount(count);
            //保存到redis
            hashOps.put(skuId.toString(),JSON.toJSONString(cart));
            //保存到数据库
            cartAsyncService.updateMysql(userId,cart,skuId.toString());
        }
    }

    /**
     * 修改购物车选中状态
     * @param cart
     */
    public void updateStatus(Cart cart) {
        String userId = this.getUserId();
        //获取内层map<skuId,cartJson>
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(KEY_PREFIX + userId);

        //获取选中状态
        Boolean check = cart.getCheck();
        //判断是否存在该skuId的map
        String skuId = cart.getSkuId().toString();
        if (hashOps.hasKey(skuId)) {
            //存在该skuId的map 修改选中状态字段
            String cartJson = hashOps.get(skuId).toString();
            //覆盖后修改状态
            cart = JSON.parseObject(cartJson,Cart.class);
            cart.setCheck(check);
            //保存到redis
            hashOps.put(skuId,JSON.toJSONString(cart));
            //异步保存到数据库
            cartAsyncService.updateMysql(userId,cart,skuId);
        }
    }
    @Async
    public void execute1(){
        try {
            System.out.println("execute1开始执行");
            TimeUnit.SECONDS.sleep(5);
            System.out.println("execute1结束执行");
            //正常响应
        } catch (InterruptedException e) {
            e.printStackTrace();
            //异常响应
        }
    }

    @Async
    public void execute2(){
        try {
            System.out.println("execute2开始执行");
            TimeUnit.SECONDS.sleep(4);

            //制造异常
            int i = 1/0;
            System.out.println("execute2结束执行");
            //正常响应
//            return AsyncResult.forValue("execute2");
        } catch (InterruptedException e) {
            e.printStackTrace();
            //异常响应
//            return AsyncResult.forExecutionException(e);
        }
    }


    public void deleteCart(String skuId) {
        //获取userId
        String userId = this.getUserId();
        //获取内层map
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(KEY_PREFIX + userId);
        //判断是否包含该skuId
        if (hashOps.hasKey(skuId)) {
            //包含 删除该skuId对应的购物车记录
            //redis 删除cartJson
            hashOps.delete(skuId);
            //根据userId 和skuId确定一条记录删除
            cartAsyncService.deleteMysqlByUserIdAndSkuId(userId,skuId);
        }
    }

    public List<Cart> queryCheckedCartByUserId(Long userId) {
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(KEY_PREFIX + userId);
        List<Object> cartJsons = hashOps.values();
        if (CollectionUtils.isEmpty(cartJsons)){
            return null;
        }

        return cartJsons.stream().map(cartJson->JSON.parseObject(cartJson.toString(),Cart.class))
                .filter(Cart::getCheck).collect(Collectors.toList());
    }
}
